#include <BuildOrderManager.h>
#include <BuildManager.h>
#include <TechManager.h>
#include <UpgradeManager.h>
#include <WorkerManager.h>
#include <SupplyManager.h>
#include <algorithm>
#include <stdarg.h>
#include <UnitGroupManager.h>
#include <InformationManager.h>
#include <math.h>
using namespace std;
using namespace BWAPI;
map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem* > >* globalUnitSet;
int y;
int currentPriority;
map<const BWAPI::Unit*,int> nextFreeTimeData;
map<BWAPI::UnitType, set<BWAPI::UnitType> > makes;
map<BWAPI::UnitType, set<BWAPI::TechType> > researches;
map<BWAPI::UnitType, set<BWAPI::UpgradeType> > upgrades;
BuildOrderManager* buildOrderManager;
BuildOrderManager::BuildOrderManager(BuildManager* buildManager, TechManager* techManager, UpgradeManager* upgradeManager, WorkerManager* workerManager, SupplyManager* supplyManager)
{
  buildOrderManager  = this;
  this->buildManager       = buildManager;
  this->techManager        = techManager;
  this->upgradeManager     = upgradeManager;
  this->workerManager      = workerManager;
  this->supplyManager      = supplyManager;
  this->usedMinerals       = 0;
  this->usedGas            = 0;
  this->dependencyResolver = false;
  this->debugMode          = false;
  UnitItem::getBuildManager() = buildManager;
  for(set<BWAPI::UnitType>::iterator i=UnitTypes::allUnitTypes().begin();i!=UnitTypes::allUnitTypes().end();i++)
  {
    makes[(*i).whatBuilds().first].insert(*i);
  }
  for(set<BWAPI::TechType>::iterator i=TechTypes::allTechTypes().begin();i!=TechTypes::allTechTypes().end();i++)
  {
    researches[i->whatResearches()].insert(*i);
  }
  for(set<BWAPI::UpgradeType>::iterator i=UpgradeTypes::allUpgradeTypes().begin();i!=UpgradeTypes::allUpgradeTypes().end();i++)
  {
    upgrades[i->whatUpgrades()].insert(*i);
  }
  // adding zerg building upgrades
  upgrades[UnitTypes::Zerg_Hive].insert(UpgradeTypes::Pneumatized_Carapace);
  upgrades[UnitTypes::Zerg_Hive].insert(UpgradeTypes::Antennae);
  upgrades[UnitTypes::Zerg_Hive].insert(UpgradeTypes::Ventral_Sacs);
  upgrades[UnitTypes::Zerg_Greater_Spire].insert(UpgradeTypes::Zerg_Flyer_Attacks);
  upgrades[UnitTypes::Zerg_Greater_Spire].insert(UpgradeTypes::Zerg_Flyer_Carapace);
}

std::map<BWAPI::UnitType, int> BuildOrderManager::requiredUnits(BWAPI::UnitType unit)
{
  std::map<BWAPI::UnitType, int> req = unit.requiredUnits();

  if (unit == BWAPI::UnitTypes::Zerg_Greater_Spire)
    req[BWAPI::UnitTypes::Zerg_Hive] = 1;
  if (unit == BWAPI::UnitTypes::Zerg_Spore_Colony)
    req[BWAPI::UnitTypes::Zerg_Evolution_Chamber] = 1;

  return req;
}

//returns the next frame that the given unit type will be ready to produce units or research tech or upgrades
int BuildOrderManager::nextFreeTime(const MetaUnit* unit)
{
  int ctime=Broodwar->getFrameCount();
  if (!unit->isCompleted() && unit->unit!=NULL)
  {
    if (!unit->isBeingConstructed())
      return -1;
  }
  int natime=ctime;
  natime=max(ctime,ctime+unit->getRemainingBuildTime());
  // Ignore the train time on Hatcheries, they are just training larva
  if (!unit->getType().isResourceDepot())
    natime=max(natime,ctime+unit->getRemainingTrainTime());
  natime=max(natime,ctime+unit->getRemainingResearchTime());
  natime=max(natime,ctime+unit->getRemainingUpgradeTime());
  if (unit->unit!=NULL)
    natime=max(natime,nextFreeTimeData[unit->unit]);
  if (this->buildManager->getBuildType((Unit*)(unit->unit))!=UnitTypes::None && !unit->hasBuildUnit() && !unit->isTraining() && !unit->isMorphing())
    natime=max(natime,ctime+this->buildManager->getBuildType((Unit*)(unit->unit)).buildTime());
  return natime;
}

//returns the next available time that at least one unit of the given type (buildings only right now) will be completed 
int BuildOrderManager::nextFreeTime(UnitType t)
{
  //if one unit of the given type is already completed, return the given frame count
  if (Broodwar->self()->completedUnitCount(t)>0)
    return Broodwar->getFrameCount();

  //resolve upgradable buildings to their higher counterpart
  UnitType upgraded = UnitTypes::None;
  if (t == UnitTypes::Zerg_Hatchery)
    upgraded = UnitTypes::Zerg_Lair;
  if (t == UnitTypes::Zerg_Lair)
    upgraded = UnitTypes::Zerg_Hive;
  if (t == UnitTypes::Zerg_Spire)
    upgraded = UnitTypes::Zerg_Greater_Spire;

  if (upgraded != UnitTypes::None)
    if (Broodwar->self()->completedUnitCount(upgraded) > 0)
      return Broodwar->getFrameCount();

  //if no units of the given type are being constructed, return -1
  if (t!=UnitTypes::Zerg_Larva)
    if (Broodwar->self()->incompleteUnitCount(t)==0)
      return -1;

  int time;
  bool setflag=false;
  for(set<MetaUnit*>::iterator i=this->MetaUnitPointers.begin();i!=this->MetaUnitPointers.end();i++)
  {
    if ((*i)->getType()!=t) continue;
    int ntime=nextFreeTime(*i);
    if (ntime>-1)
    {
      //set time to the earliest available time
      if (!setflag || ntime<time)
      {
        time=ntime;
        setflag=true;
      }
    }
  }
  if (t.supplyRequired()>0)
  {
    if (Broodwar->self()->supplyUsed()+t.supplyRequired()>Broodwar->self()->supplyTotal())
    {
      time=this->supplyManager->getSupplyTime(Broodwar->self()->supplyUsed()+t.supplyRequired());
    }
  }
  if (setflag)
    return time;

  //we can get here if construction has been halted by an SCV
  return -1;
}
  
int BuildOrderManager::nextFreeTime(BWAPI::TechType t)
{
  if (t == BWAPI::TechTypes::None)
    return 0;

  if (Broodwar->self()->hasResearched(t))
    return 0;

  // This is not 100% accurate, but a rough estimate is good enough for the one
  // edge case (Lurker production) that this function is even used for 
  if (Broodwar->self()->isResearching(t))
    return t.researchTime();

  return -1;
}

//returns the next available time that the given unit will be able to train the given unit type
//takes into account required units
//todo: take into account required tech and supply
int BuildOrderManager::nextFreeTime(const MetaUnit* unit, UnitType t)
{
  std::map<BWAPI::UnitType, int> req = this->requiredUnits(t);
  int time=nextFreeTime(unit);
  for(map<UnitType,int>::const_iterator i=req.begin();i!=req.end();i++)
  {
    int ntime=nextFreeTime(i->first);
    if (ntime==-1)
      return -1;
    if (ntime>time)
      time=ntime;
  }

  // tech check for lurkers
  int ntime = nextFreeTime(t.requiredTech());
  if (ntime == -1)
    return -1;
  if (ntime>time)
    time = ntime;

  return time;
}

int BuildOrderManager::nextFreeTime(const MetaUnit* unit, TechType t)
{
  //if something else is already researching it, this unit will never be able to research it
  if (Broodwar->self()->isResearching(t))
    return -1;

  return nextFreeTime(unit);
}

int BuildOrderManager::nextFreeTime(const MetaUnit* unit, UpgradeType t)
{
  int time=nextFreeTime(unit);
  // Check requirement
  BWAPI::UnitType req = t.whatsRequired(Broodwar->self()->getUpgradeLevel(t) + 1);
  // Special case for adrenal bug
  if (t == UpgradeTypes::Adrenal_Glands)
    req = UnitTypes::Zerg_Hive;

  if (req != UnitTypes::None) {
    int ntime = nextFreeTime(req);
    if (ntime == -1)
      return -1;
    if (ntime>time)
      time = ntime;
  }

  if (!Broodwar->self()->isUpgrading(t))
    return time;

  for(std::set<MetaUnit*>::iterator i=this->MetaUnitPointers.begin();i!=this->MetaUnitPointers.end();i++)
  {
    // Check the list of upgrades this metaunit can do, and continue if the type is 
    // not on the list. Do it this way because multiple buildings can upgrade the same thing
    if (upgrades[(*i)->getType()].find(t) != upgrades[(*i)->getType()].end()) continue;
    if ((*i)->isUpgrading() && (*i)->getUpgrade()==t)
    {
      time=max(time,nextFreeTime(*i));
    }
  }
  return time;
}

bool BuildOrderManager::isResourceLimited()
{
  return this->isMineralLimited && this->isGasLimited;
}
//returns the set of unit types the given unit will be able to make at the given time
set<BWAPI::UnitType> BuildOrderManager::unitsCanMake(MetaUnit* builder, int time)
{
  set<BWAPI::UnitType> result;
  for(set<BWAPI::UnitType>::iterator i=makes[builder->getType()].begin();i!=makes[builder->getType()].end();i++)
  {
    int t=nextFreeTime(builder,*i);
    if (t>-1 && t<=time)
      result.insert(*i);
  }
  return result;
}

set<BWAPI::TechType> BuildOrderManager::techsCanResearch(MetaUnit* techUnit, int time)
{
  set<BWAPI::TechType> result;
  for(set<BWAPI::TechType>::iterator i=researches[techUnit->getType()].begin();i!=researches[techUnit->getType()].end();i++)
  {
    int t=nextFreeTime(techUnit,*i);
    if (t>-1 && t<=time)
      result.insert(*i);
  }
  return result;
}

set<BWAPI::UpgradeType> BuildOrderManager::upgradesCanResearch(MetaUnit* techUnit, int time)
{
  set<BWAPI::UpgradeType> result;
  for(set<BWAPI::UpgradeType>::iterator i=upgrades[techUnit->getType()].begin();i!=upgrades[techUnit->getType()].end();i++)
  {
    int t=nextFreeTime(techUnit,*i);
    if (t>-1 && t<=time)
      result.insert(*i);
  }
  return result;
}

//prefer unit types that have larger remaining unit counts
//if we need a tie-breaker, we prefer cheaper units
bool unitTypeOrderCompare(const pair<BWAPI::UnitType, int >& a, const pair<BWAPI::UnitType, int >& b)
{
  int rA=a.second;
  int rB=b.second;
  int pA=a.first.mineralPrice()+a.first.gasPrice();
  int pB=b.first.mineralPrice()+b.first.gasPrice();
  return rA>rB || (rA == rB && pA<pB);
}

UnitType getUnitType(set<UnitType>& validUnitTypes,vector<pair<BWAPI::UnitType, int > >& unitCounts)
{
  UnitType answer=UnitTypes::None;
  //sort unit counts in descending order of size
  sort(unitCounts.begin(),unitCounts.end(),unitTypeOrderCompare);
  for(vector<pair<BWAPI::UnitType, int > >::iterator i=unitCounts.begin();i!=unitCounts.end();i++)
  {
    //use the first valid unit type we find
    if (validUnitTypes.find(i->first)!=validUnitTypes.end())
    {
      answer=i->first;
      i->second--;
      if (i->second<=0)
      {
        unitCounts.erase(i);
      }
      break;
    }
  }
  return answer;
}

pair<TechType,UpgradeType> getTechOrUpgradeType(set<TechType>& validTechTypes, set<UpgradeType>& validUpgradeTypes, list<TechItem> &remainingTech)
{
  pair<TechType,UpgradeType> answer(TechTypes::None,UpgradeTypes::None);
  for(list<TechItem>::iterator i=remainingTech.begin();i!=remainingTech.end();i++)
  {
    //use the first valid unit type we find
    TechType t=(*i).techType;
    UpgradeType u=(*i).upgradeType;
    if (t!=TechTypes::None)
    {
      if (validTechTypes.find(t)!=validTechTypes.end())
      {
        answer.first=t;
        remainingTech.erase(i);
        break;
      }
    }
    else if (u!=UpgradeTypes::None)
    {
      if (validUpgradeTypes.find(u)!=validUpgradeTypes.end())
      {
        answer.second=u;
        remainingTech.erase(i);
        break;
      }
    }
  }
  return answer;
}
bool factoryCompare(const BuildOrderManager::MetaUnit* a, const BuildOrderManager::MetaUnit* b)
{
  return buildOrderManager->nextFreeTime(a)>buildOrderManager->nextFreeTime(b);
}
bool BuildOrderManager::updateUnits()
{
  // Initial check to save time
  if (globalUnitSet->empty())
  {
    //false = not resource limited
    return false;
  }

  set<MetaUnit*> allUnits = this->MetaUnitPointers;

  //sanity check the data (not sure if we need to, but just in case)
  map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem* > >::iterator i2;
  for(map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem* > >::iterator i=globalUnitSet->begin();i!=globalUnitSet->end();i=i2)
  {
    i2=i;
    i2++;
    map<BWAPI::UnitType, UnitItem* >::iterator j2;
    for(map<BWAPI::UnitType, UnitItem* >::iterator j=i->second.begin();j!=i->second.end();j=j2)
    {
      j2=j;
      j2++;
      if (j->second==NULL || j->second->getRemainingCount()==0)
      {
        i->second.erase(j);
      }
    }
    if (i->second.empty())
      globalUnitSet->erase(i);
  }

  // Second check to see if loop removed
  if (globalUnitSet->empty())
  {
    //false = not resource limited
    return false;
  }

  //get the set of factory Units
  list<MetaUnit*> factories;
  for(set<MetaUnit*>::iterator i=allUnits.begin();i!=allUnits.end();i++)
  {
    MetaUnit* u=*i;
    UnitType type=u->getType();
    //only add the factory if it hasn't been reserved and if its a builder type that we need
    if (globalUnitSet->find(type)!=globalUnitSet->end() && this->reservedUnits.find(u)==this->reservedUnits.end())
      factories.push_back(u);
  }
  factories.sort(factoryCompare);

  //find the sequence of interesting points in time in the future
  set<int> times;
  //iterate through each type of builder
  for(map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem* > >::iterator i=globalUnitSet->begin();i!=globalUnitSet->end();i++)
  {
    UnitType unitType=i->first;//builder type

    //iterate through all our factory Units
    for(list<MetaUnit*>::iterator f=factories.begin();f!=factories.end();f++)
    {
      MetaUnit* u=*f;
      UnitType type=u->getType();
      if (type==i->first)//only look at units of this builder type
      {
        //iterate over all the types of units we want to make with this builder type
        for(map<BWAPI::UnitType, UnitItem* >::iterator j=i->second.begin();j!=i->second.end();j++)
        {
          //add the time to the sequence if it is in the future (and not -1)
          int time=nextFreeTime(*f,j->first);
          if (time>-1)
            times.insert(time);
        }
      }
    }
  }

  //get the remaining unit counts for each type of unit we want to make
  vector<pair<BWAPI::UnitType, int > > remainingUnitCounts;
  for(map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem* > >::iterator i=globalUnitSet->begin();i!=globalUnitSet->end();i++)
  {
    for(map<BWAPI::UnitType, UnitItem* >::iterator j=i->second.begin();j!=i->second.end();j++)
    {
      remainingUnitCounts.push_back(make_pair(j->first,j->second->getRemainingCount(currentlyPlannedCount[j->first])));
    }
  }

  //reserve units and resources for later

  //iterate through time (this gives earlier events higher priority)
  times.insert(Broodwar->getFrameCount());
  for(set<int>::iterator t=times.begin();t!=times.end();t++)
  {
    int ctime=*t;

    //remove all factories that have been reserved
    list<MetaUnit*>::iterator f2;
    for(list<MetaUnit*>::iterator f=factories.begin();f!=factories.end();f=f2)
    {
      f2=f;
      f2++;
      if (this->reservedUnits.find(*f)!=this->reservedUnits.end())
        factories.erase(f);
    }
    //iterate through all factories that haven't been reserved yet
    for(list<MetaUnit*>::iterator f=factories.begin();f!=factories.end();f++)
    {
      MetaUnit* factory=*f;
      //get a unit type, taking into account the remaining unit counts and the set of units this factory can make at time ctime
      UnitType t=getUnitType(unitsCanMake(*f,ctime),remainingUnitCounts);
      if (t==UnitTypes::None)
        continue;
      int btime=ctime;
      if (factory->getType().isWorker())
        btime=ctime+24*4;
      currentlyPlannedCount[t]++;
      this->reserveResources(factory,t);
      this->reservedUnits.insert(factory);
      this->savedPlan.push_back(Type(t,factory,currentPriority,ctime));
      if (this->isResourceLimited())
        return true;
    }
  }
  //false = not resource limited
  return false;
}
void BuildOrderManager::update()
{
  int time=Broodwar->getFrameCount();
  if (time>=this->nextUpdateFrame)
  {
    // Check reactors first
    for (std::map<BWAPI::UnitType, std::list<UnitReactor> >::iterator iter = reactors.begin(); iter != reactors.end(); ++iter) {
      // Make sure there are things we can do
      if (iter->second.size() > 0) {
        // Check for enemy units
        int enemyUnitCount = TheInformationManager->getUnitTypeCount(iter->first);

        for (std::list<UnitReactor>::iterator reactor = iter->second.begin(); reactor != iter->second.end(); ++reactor) {
          if (enemyUnitCount >= reactor->threshold) {
            // enemy has amassed a dangerous number of said unit. Build up reacting group!
            BWAPI::UnitType myUnit = reactor->playerUnit;         
            int targetNum = (int)ceil(reactor->ratio * (float)enemyUnitCount); 
            int unitsPlanned = this->getPlannedCount(myUnit);
            int numToBuild = targetNum - unitsPlanned;
            // Skip if we have enough
            if (numToBuild <= 0)
              continue;

            // to be tweaked -- presently assuming that higher food count of my unit
            // means it's more urgent to build ( coz it's gonna be a costlier unit)!
            // Doug: changing it to sum cost instead of supply, normalized to 3:1 ratio.
            // The multiplier will be based off of how many we need on the field,
            // not necessarily how many we need this iteration
            int unitsHave = BWAPI::Broodwar->self()->completedUnitCount(myUnit);
            int priority = (targetNum - unitsHave) * (myUnit.mineralPrice()/3 + myUnit.gasPrice()) / 5;
            // if it's a double unit, cut priority in half
            if (myUnit.isTwoUnitsInOneEgg()) priority = priority / 2;

            // if it's a building, seed it
            if (myUnit.isBuilding()) {
              priority = 50; // will likely wait until Creep Colonies are available

              // for now, just use the furthest base from our home
              BWTA::BaseLocation* home = BWTA::getStartLocation(BWAPI::Broodwar->self());
              BWTA::BaseLocation* seed = home;
              double far = 0;
              for each (Base* base in TheBaseManager->getActiveBases()) {
                double dist = home->getAirDistance(base->getBaseLocation());
                if (dist > far) {
                  far = dist;
                  seed = base->getBaseLocation();
                }
              }
              
              this->buildAdditional(numToBuild, myUnit, priority, TilePosition( TheDefenseManager->getChokePointClosestToBase(seed)->getCenter()) );
              Broodwar->printf("Seeding %d %s here", numToBuild, myUnit.getName().c_str());
              Broodwar->pingMinimap(seed->getPosition());
            } else {
              this->buildAdditional(numToBuild, myUnit, priority);
            }

            BWAPI::Broodwar->printf ("Reacting to %d %s with %d of %s|priority %d!",
              enemyUnitCount, iter->first.getName().c_str(), numToBuild, 
              myUnit.getName().c_str(), priority);
            // TODO: Need some metric to stop using this reactor
          }
        }
      }
    }
    // Check auto upgrades
    for (std::map<std::pair<BWAPI::UnitType, BWAPI::Player*>, std::list<AutoUpgrade> >::iterator upEntry = autoUpgrades.begin(); upEntry != autoUpgrades.end(); ++upEntry) {
      // make sure there is at least one item
      if (upEntry->second.size() > 0) {
        // get unit count for this player
        int count;
        if (upEntry->first.second == BWAPI::Broodwar->self()) {
          count = SelectAll(upEntry->first.second, upEntry->first.first).size();
        } else {
          count = TheInformationManager->getUnitTypeCount(upEntry->first.first);
        }

        for (std::list<AutoUpgrade>::iterator iter = upEntry->second.begin(); iter != upEntry->second.end();){
          if (count > iter->threshold) {
            upgrade(iter->level, iter->upgrade, iter->priority);
            autoUpgrades[upEntry->first].erase(iter++); // increment here before iter gets invalidated
          } else
            ++iter;
        }
      }
    }

    updatePlan();
    this->nextUpdateFrame=time+24*3;
  }
  y=0;
  this->reservedResources.clear();
  this->reservedUnits.clear();
  this->isGasLimited=false;
  this->isMineralLimited=false;
  debug("time=%d",time);
  list<Type>::iterator i2;
  for(list<Type>::iterator i=this->savedPlan.begin();i!=this->savedPlan.end();i=i2)
  {
    i2=i;
    i2++;
    UnitType unit=(*i).unitType;
    TechType tech=(*i).techType;
    UpgradeType upgrade=(*i).upgradeType;
    MetaUnit* factory=(*i).unit;
    int priority=(*i).priority;
    int ctime=(*i).time;
    if (ctime<Broodwar->getFrameCount())
      ctime=Broodwar->getFrameCount();
    int btime=ctime;
    if (factory->getType().isWorker())
      btime=ctime+24*4;
    if (unit!=UnitTypes::None)
    {
      if ((*i).time>=time)
        debug("%s at %d",unit.getName().c_str(),(*i).time);
      else
        debug("%s as soon as possible",unit.getName().c_str());
      // if this is a unit from a Larva, only queue it if the Larva will be available before the next plan update
      if (ctime<=time && hasResources(unit,btime) &&
          (factory->getType() != BWAPI::UnitTypes::Zerg_Larva || factory->larvaSpawnTime < this->nextUpdateFrame)) {
        TilePosition tp=this->items[priority].units[factory->getType()][unit].decrementAdditional();
        if (!factory->hasAddon())
          this->buildManager->build(unit,tp,true);
        nextFreeTimeData[factory->unit]=time+24*6;
        if (this->debugMode) Broodwar->printf("Build %s",unit.getName().c_str());
        this->spendResources(unit);
        TheInformationManager->addItemOrder(unit.getName().c_str());
        savedPlan.erase(i);
      }
      else
        this->reserveResources(factory,unit);
    }
    else if (tech!=TechTypes::None)
    {
      if ((*i).time>=time)
        debug("%s at %d",tech.getName().c_str(),(*i).time);
      else
        debug("%s as soon as possible",tech.getName().c_str());
      if (ctime<=time && hasResources(tech,btime))
      {
        if (this->debugMode) Broodwar->printf("Research %s",tech.getName().c_str());
        this->techManager->research(tech);
        nextFreeTimeData[factory->unit]=time+24*6;
        this->spendResources(tech);
        TheInformationManager->addItemOrder(tech.getName().c_str());
        savedPlan.erase(i);
      }
      else
        this->reserveResources(factory,tech);
    }
    else if (upgrade!=UpgradeTypes::None)
    {
      if ((*i).time>=time)
        debug("%s at %d",upgrade.getName().c_str(),(*i).time);
      else
        debug("%s as soon as possible",upgrade.getName().c_str());
      if (ctime<=time && hasResources(upgrade,btime))
      {
        if (this->debugMode) Broodwar->printf("Upgrade %s",upgrade.getName().c_str());
        if (this->upgradeManager->upgrade(upgrade)) {
          nextFreeTimeData[factory->unit]=time+24*6;
          this->spendResources(upgrade);
          TheInformationManager->addItemOrder(upgrade.getName().c_str());
          savedPlan.erase(i);
        }
      }
      else
        this->reserveResources(factory,upgrade);
    }
  }
}
void BuildOrderManager::updatePlan()
{
  this->savedPlan.clear();
  
  this->MetaUnits.clear();
  this->MetaUnitPointers.clear();
  for(set<Unit*>::const_iterator i=BWAPI::Broodwar->self()->getUnits().begin();i!=BWAPI::Broodwar->self()->getUnits().end();i++)
  {
    MetaUnits.push_back(MetaUnit(*i));
    if ((*i)->getType()==UnitTypes::Zerg_Hatchery || (*i)->getType()==UnitTypes::Zerg_Lair || (*i)->getType()==UnitTypes::Zerg_Hive)
    {
      // Pushing the next larva, if we aren't capped
      if ((*i)->getRemainingTrainTime()>0)
        MetaUnits.push_back(MetaUnit(Broodwar->getFrameCount()+(*i)->getRemainingTrainTime()));
      // Pushing two additional larva
      MetaUnits.push_back(MetaUnit(Broodwar->getFrameCount()+(*i)->getRemainingTrainTime()+334*1));
      MetaUnits.push_back(MetaUnit(Broodwar->getFrameCount()+(*i)->getRemainingTrainTime()+334*2));
    }
  }
  for(list<MetaUnit>::iterator i=MetaUnits.begin();i!=MetaUnits.end();i++)
  {
    this->MetaUnitPointers.insert(&(*i));
  }
  for(set<UnitType>::iterator i=UnitTypes::allUnitTypes().begin();i!=UnitTypes::allUnitTypes().end();i++)
  {
    currentlyPlannedCount[*i]=this->buildManager->getPlannedCount(*i);
  }
  map< int, PriorityLevel >::iterator l2;
  for(map< int, PriorityLevel >::iterator l=items.begin();l!=items.end();l=l2)
  {
    l2=l;
    l2++;
    if (l->second.techs.empty() && l->second.units.empty())
      items.erase(l);
  }
  if (items.empty()) return;
  map< int, PriorityLevel >::iterator l=items.end();
  l--;
  this->reservedResources.clear();
  this->reservedUnits.clear();
  this->isGasLimited=false;
  this->isMineralLimited=false;
  y=5;

  //Iterate through priority levels in decreasing order
  //---------------------------------------------------------------------------------------------------------
  for(;l!=items.end();l--)
  {
    currentPriority=l->first;

    //First consider all techs and upgrades for this priority level
    if (!l->second.techs.empty()) {
      set<UnitType> techUnitTypes;
      for(list<TechItem>::iterator i=l->second.techs.begin();i!=l->second.techs.end();i++)
      {
        if (i->techType!=TechTypes::None)
          techUnitTypes.insert(i->techType.whatResearches());
        if (i->upgradeType!=UpgradeTypes::None)
          techUnitTypes.insert(i->upgradeType.whatUpgrades());

        // Special zerg cases
        if (i->upgradeType == UpgradeTypes::Pneumatized_Carapace)
          techUnitTypes.insert(UnitTypes::Zerg_Hive);
        if (i->upgradeType == UpgradeTypes::Antennae)
          techUnitTypes.insert(UnitTypes::Zerg_Hive);
        if (i->upgradeType == UpgradeTypes::Ventral_Sacs)
          techUnitTypes.insert(UnitTypes::Zerg_Hive);
        if (i->upgradeType == UpgradeTypes::Zerg_Flyer_Attacks)
          techUnitTypes.insert(UnitTypes::Zerg_Greater_Spire);
        if (i->upgradeType == UpgradeTypes::Zerg_Flyer_Carapace)
          techUnitTypes.insert(UnitTypes::Zerg_Greater_Spire);
      }
      set<MetaUnit*> allUnits=this->MetaUnitPointers;
      //get the set of tech Units
      set<MetaUnit*> techUnits;
      if (!techUnitTypes.empty()) {
        for(set<MetaUnit*>::iterator i=allUnits.begin();i!=allUnits.end();i++)
        {
          MetaUnit* u=*i;
          UnitType type=u->getType();
          //only add the factory if it hasn't been reserved and if its a builder type that we need
          if (techUnitTypes.find(type)!=techUnitTypes.end() && this->reservedUnits.find(u)==this->reservedUnits.end())
            techUnits.insert(u);
        }
      }

      //find the sequence of interesting points in time in the future
      set<int> times;
      for(set<MetaUnit*>::iterator i=techUnits.begin();i!=techUnits.end();i++)
      {
        //add the time to the sequence if it is in the future (and not -1)
        int time=nextFreeTime(*i);
        if (time>-1)
          times.insert(time);
      }

      //get the remaining tech
      list<TechItem > remainingTech=l->second.techs;

      if (this->dependencyResolver)
      {
        //check dependencies
        for(list<TechItem>::iterator i=remainingTech.begin();i!=remainingTech.end();i++)
        {
          TechType t=i->techType;
          UpgradeType u=i->upgradeType;
          if (t!=TechTypes::None)
          {
            if (this->getPlannedCount(t.whatResearches())==0)
            {
              this->build(1,t.whatResearches(),l->first+1);
            }
            //Lurker tech also requires Lair
            if (t == TechTypes::Lurker_Aspect) {
              if (this->getPlannedCount(UnitTypes::Zerg_Lair) == 0) {
                this->build(1, UnitTypes::Zerg_Lair, l->first+1);
              }
            }
            //also check to see if we have enough gas, or a refinery planned
            // I think this will cause a mineral leak - it will order refineries
            // to be built and if no location can be found, the minerals will be
            // assumed used until a base is acquired and one is built. We will let
            // BaseManager handle this for now.
            // if (t.gasPrice()>BWAPI::Broodwar->self()->cumulativeGas()-this->usedGas)
            // {
            //   UnitType refinery=Broodwar->self()->getRace().getRefinery();
            //   if (this->getPlannedCount(refinery)==0)
            //     this->build(1,refinery,l->first+1);
            // }
          }
          else if (u!=UpgradeTypes::None)
          {
            if (this->getPlannedCount(u.whatUpgrades())==0)
              this->build(1,u.whatUpgrades(),l->first+1);
            if (this->getPlannedCount(u.whatsRequired(i->level)) == 0)
              this->build(1, u.whatsRequired(i->level), l->first+1);
            // Fixing missing requirement for Adrenal Glands -> Hive
            if (u == UpgradeTypes::Adrenal_Glands)
              if (this->getPlannedCount(UnitTypes::Zerg_Hive) == 0)
                this->build(1, UnitTypes::Zerg_Hive, l->first+1);

            // Temporary fix for bug in BWAPI - forcing Lair/Spire build if we
            // have a Hive/Greater Spire for upgrading
            if (u.whatUpgrades() == UnitTypes::Zerg_Lair && buildManager->getStartedCount(UnitTypes::Zerg_Hive))
              this->build(1, UnitTypes::Zerg_Lair, l->first+1);
            if (u.whatUpgrades() == UnitTypes::Zerg_Spire && buildManager->getStartedCount(UnitTypes::Zerg_Greater_Spire))
              this->build(1, UnitTypes::Zerg_Spire, l->first+1);

            // Temporary fix for bug in BWAPI - forcing Lair build if we are
            // trying to do a t2 upgrade
            if (i->level >= 2 && buildManager->getStartedCount(UnitTypes::Zerg_Hive))
              this->build(1, UnitTypes::Zerg_Lair, l->first+1);

            //also check to see if we have enough gas, or a refinery planned
            // I think this will cause a mineral leak - it will order refineries
            // to be built and if no location can be found, the minerals will be
            // assumed used until a base is acquired and one is built. We will let
            // BaseManager handle this for now.
            // if (u.gasPrice()+u.gasPriceFactor()*(BWAPI::Broodwar->self()->getUpgradeLevel(u)-1)>BWAPI::Broodwar->self()->cumulativeGas()-this->usedGas)
            // {
            //   UnitType refinery=Broodwar->self()->getRace().getRefinery();
            //   if (this->getPlannedCount(refinery)==0)
            //     this->build(1,refinery,l->first+1);
            // }
          }
        }
      }

      //reserve units and resources for later

      //iterate through time (this gives earlier events higher priority)
      for(set<int>::iterator t=times.begin();t!=times.end();t++)
      {
        int ctime=*t;

        //remove all tech units that have been reserved
        set<MetaUnit*>::iterator i2;
        for(set<MetaUnit*>::iterator i=techUnits.begin();i!=techUnits.end();i=i2)
        {
          i2=i;
          i2++;
          if (this->reservedUnits.find(*i)!=this->reservedUnits.end())
            techUnits.erase(i);
        }
        //iterate through all tech units that haven't been reserved yet
        for(set<MetaUnit*>::iterator i=techUnits.begin();i!=techUnits.end();i++)
        {
          MetaUnit* techUnit=*i;
          //get a unit type, taking into account the remaining unit counts and the set of units this factory can make right now
          pair< TechType,UpgradeType > p=getTechOrUpgradeType(techsCanResearch(*i,ctime),upgradesCanResearch(*i,ctime),remainingTech);
          TechType t=p.first;
          UpgradeType u=p.second;
          if (t==TechTypes::None && u==UpgradeTypes::None)
            continue;
          this->reservedUnits.insert(techUnit);
          if (t!=TechTypes::None)
          {
            this->reserveResources(techUnit,t);
            this->savedPlan.push_back(Type(t,techUnit,currentPriority,ctime));
          }
          else if (u!=UpgradeTypes::None)
          {
            this->reserveResources(techUnit,u);
            this->savedPlan.push_back(Type(u,techUnit,currentPriority,ctime));
          }
          if (this->isResourceLimited())
            return;
        }
      }
    }




    //-------------------------------------------------------------------------------------------------------
    //Next consider all buildings and units for this priority level
    if (!l->second.units.empty()) {
      map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem* > > buildings;
      map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem* > > units;
      for(map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem > >::iterator i=l->second.units.begin();i!=l->second.units.end();i++)
      {
        for(map<BWAPI::UnitType, UnitItem >::iterator j=i->second.begin();j!=i->second.end();j++)
        {
          if (j->first.isBuilding())
            buildings[i->first][j->first]=&(j->second);
          else
            units[i->first][j->first]=&(j->second);
          if (this->dependencyResolver)
          {
            //check dependencies (required units)
            std::map<BWAPI::UnitType, int> reqU = this->requiredUnits(j->first);
            for(map<BWAPI::UnitType, int>::const_iterator k=reqU.begin();k!=reqU.end();k++)
            {
              if (k->first == UnitTypes::Zerg_Larva) continue;
              // If the source is a unit, we need to get a more accurate count
              int count = 1;
              if (!k->first.isBuilding()) {
                count = j->second.getRemainingCount();
              }
              if (this->getPlannedCount(k->first) < count)
              {
                this->build(count,k->first,l->first);
              }
            }
            //check dependencies (required tech)
            TechType req = j->first.requiredTech();
            if (req != BWAPI::TechTypes::None) {
              this->research(req, l->first);
            }
            //also check to see if we have enough gas, or a refinery planned
            // I think this will cause a mineral leak - it will order refineries
            // to be built and if no location can be found, the minerals will be
            // assumed used until a base is acquired and one is built. We will let
            // BaseManager handle this for now.
            // if (j->first.gasPrice()>BWAPI::Broodwar->self()->cumulativeGas()-this->usedGas)
            // {
            //   if (j->first!=UnitTypes::Zerg_Larva && j->first!=UnitTypes::Zerg_Egg && j->first!=UnitTypes::Zerg_Lurker_Egg && j->first!=UnitTypes::Zerg_Cocoon)
            //   {
            //     UnitType refinery=Broodwar->self()->getRace().getRefinery();
            //     if (this->getPlannedCount(refinery)==0)
            //       this->build(1,refinery,l->first);
            //   }
            // }
          }
        }
      }
      globalUnitSet=&buildings;
      if (updateUnits()) return;
      globalUnitSet=&units;
      if (updateUnits()) return;
    }

    this->removeCompletedItems(&(l->second));
  }
  debug("unit-limited");
}

string BuildOrderManager::getName() const
{
  return "Build Order Manager";
}
void BuildOrderManager::build(int count, BWAPI::UnitType t, int priority, BWAPI::TilePosition seedPosition)
{
  if (t == BWAPI::UnitTypes::None || t == BWAPI::UnitTypes::Unknown) return;
  if (seedPosition == BWAPI::TilePositions::None || seedPosition == BWAPI::TilePositions::Unknown)
    seedPosition=BWAPI::Broodwar->self()->getStartLocation();
  
  if (items[priority].units[t.whatBuilds().first].find(t)==items[priority].units[t.whatBuilds().first].end())
    items[priority].units[t.whatBuilds().first].insert(make_pair(t,UnitItem(t)));
  items[priority].units[t.whatBuilds().first][t].setNonAdditional(count,seedPosition);
  nextUpdateFrame=0;
}

void BuildOrderManager::buildAdditional(int count, BWAPI::UnitType t, int priority, BWAPI::TilePosition seedPosition)
{
  if (t == BWAPI::UnitTypes::None || t == BWAPI::UnitTypes::Unknown) return;
  if (seedPosition == BWAPI::TilePositions::None || seedPosition == BWAPI::TilePositions::Unknown)
    seedPosition=BWAPI::Broodwar->self()->getStartLocation();

  if (items[priority].units[t.whatBuilds().first].find(t)==items[priority].units[t.whatBuilds().first].end())
    items[priority].units[t.whatBuilds().first].insert(make_pair(t,UnitItem(t)));
  items[priority].units[t.whatBuilds().first][t].addAdditional(count,seedPosition);
  nextUpdateFrame=0;
}

void BuildOrderManager::research(BWAPI::TechType t, int priority)
{
  if (t==BWAPI::TechTypes::None || t==BWAPI::TechTypes::Unknown) return;

  items[priority].techs.push_back(TechItem(t));
  nextUpdateFrame=0;
}

void BuildOrderManager::upgrade(int level, BWAPI::UpgradeType t, int priority)
{
  if (t==BWAPI::UpgradeTypes::None || t==BWAPI::UpgradeTypes::Unknown) return;
  items[priority].techs.push_back(TechItem(t,level));
  nextUpdateFrame=0;
}

bool BuildOrderManager::hasResources(std::pair<int, BuildOrderManager::Resources> res)
{
  bool mineralLimited=false;
  bool gasLimited=false;
  this->reserveResources(res);
  double m=BWAPI::Broodwar->self()->cumulativeMinerals()-this->usedMinerals;
  double g=BWAPI::Broodwar->self()->cumulativeGas()-this->usedGas;
  for(map<int, Resources>::iterator i=this->reservedResources.begin();i!=this->reservedResources.end();i++)
  {
    double t=i->first-Broodwar->getFrameCount();
    m-=i->second.minerals;
    g-=i->second.gas;
    if (m+t*this->workerManager->getMineralRate()<0)
      mineralLimited=true;
    if (g+t*this->workerManager->getGasRate()<0)
      gasLimited=true;
  }
  this->unreserveResources(res);
  this->isMineralLimited = this->isMineralLimited || mineralLimited;
  this->isGasLimited = this->isGasLimited || gasLimited;
  return (!mineralLimited || res.second.minerals==0) && (!gasLimited || res.second.gas==0);
}
bool BuildOrderManager::hasResources(BWAPI::UnitType t)
{
  bool ret=hasResources(t,Broodwar->getFrameCount());
  return ret;
}
bool BuildOrderManager::hasResources(BWAPI::TechType t)
{
  return hasResources(t,Broodwar->getFrameCount());
}
bool BuildOrderManager::hasResources(BWAPI::UpgradeType t)
{
  return hasResources(t,Broodwar->getFrameCount());
}
bool BuildOrderManager::hasResources(BWAPI::UnitType t, int time)
{
  pair<int, Resources> res;
  res.first=time;
  res.second.minerals=t.mineralPrice();
  res.second.gas=t.gasPrice();
  bool ret=hasResources(res);
  return ret;
}

bool BuildOrderManager::hasResources(BWAPI::TechType t, int time)
{
  pair<int, Resources> res;
  res.first=time;
  res.second.minerals=t.mineralPrice();
  res.second.gas=t.gasPrice();
  return hasResources(res);
}

bool BuildOrderManager::hasResources(BWAPI::UpgradeType t, int time)
{
  pair<int, Resources> res;
  res.first=time;
  res.second.minerals=t.mineralPrice()+t.mineralPriceFactor()*this->upgradeManager->getPlannedLevel(t);
  res.second.gas=t.gasPrice()+t.gasPriceFactor()*this->upgradeManager->getPlannedLevel(t);
  return hasResources(res);
}

void BuildOrderManager::spendResources(BWAPI::UnitType t)
{
  //BWAPI::Broodwar->printf("Spending resources on unit %s", t.getName().c_str());
  this->usedMinerals+=t.mineralPrice();
  this->usedGas+=t.gasPrice();
}

void BuildOrderManager::spendResources(BWAPI::TechType t)
{
  //BWAPI::Broodwar->printf("Spending resources on tech %s", t.getName().c_str());
  this->usedMinerals+=t.mineralPrice();
  this->usedGas+=t.gasPrice();
}

void BuildOrderManager::spendResources(BWAPI::UpgradeType t)
{
  //BWAPI::Broodwar->printf("Spending resources on upgrade %s", t.getName().c_str());
  this->usedMinerals+=t.mineralPrice()+t.mineralPriceFactor()*this->upgradeManager->getPlannedLevel(t);
  this->usedGas+=t.gasPrice()+t.gasPriceFactor()*this->upgradeManager->getPlannedLevel(t);
}

//returns the BuildOrderManager's planned count of units for this type
int BuildOrderManager::getPlannedCount(BWAPI::UnitType t)
{
  //builder unit type
  UnitType builder=t.whatBuilds().first;

  int c=this->buildManager->getPlannedCount(t);

  //sum all the remaining units for every priority level
  for(map<int, PriorityLevel>::iterator p=items.begin();p!=items.end();p++)
  {
    map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem > >* units=&(p->second.units);
    map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem > >::iterator i=units->find(builder);

    if (i!=units->end())
    {
      map<BWAPI::UnitType, UnitItem >* units2=&(i->second);
      map<BWAPI::UnitType, UnitItem >::iterator j=units2->find(t);
      if (j!=units2->end())
      {
        c+=j->second.getRemainingCount(c);
      }
    }
  }
  if (t==UnitTypes::Zerg_Hatchery)
    c+=this->buildManager->getStartedCount(UnitTypes::Zerg_Lair);
  if (t==UnitTypes::Zerg_Lair)
    c+=this->buildManager->getStartedCount(UnitTypes::Zerg_Hive);
  if (t==UnitTypes::Zerg_Spire)
    c+=this->buildManager->getStartedCount(UnitTypes::Zerg_Greater_Spire);
  return c;
}

//returns the BuildOrderManager's planned count of units for this type
int BuildOrderManager::getPlannedCount(BWAPI::UnitType t, int minPriority)
{
  //builder unit type
  UnitType builder=t.whatBuilds().first;

  int c=this->buildManager->getPlannedCount(t);

  //sum all the remaining units for every priority level
  for(map<int, PriorityLevel>::iterator p=items.begin();p!=items.end();p++)
  {
    if (p->first<minPriority) continue; //don't consider planned units below min priority
    map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem > >* units=&(p->second.units);
    map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem > >::iterator i=units->find(builder);

    if (i!=units->end())
    {
      map<BWAPI::UnitType, UnitItem >* units2=&(i->second);
      map<BWAPI::UnitType, UnitItem >::iterator j=units2->find(t);
      if (j!=units2->end())
      {
        c+=j->second.getRemainingCount(c);
      }
    }
  }
  if (t==UnitTypes::Zerg_Hatchery)
    c+=this->getPlannedCount(UnitTypes::Zerg_Lair);
  if (t==UnitTypes::Zerg_Lair)
    c+=this->getPlannedCount(UnitTypes::Zerg_Hive);
  return c;
}

//reserves resources for this unit type
pair<int, BuildOrderManager::Resources> BuildOrderManager::reserveResources(MetaUnit* builder, UnitType unitType)
{
  int t=Broodwar->getFrameCount();
  if (builder)
    t=nextFreeTime(builder,unitType);
  pair<int, Resources> ret;
  ret.first=t;
  ret.second.minerals=unitType.mineralPrice();
  ret.second.gas=unitType.gasPrice();
  reserveResources(ret);
  return ret;
}
//reserves resources for this tech type
pair<int, BuildOrderManager::Resources> BuildOrderManager::reserveResources(MetaUnit* techUnit, TechType techType)
{
  int t=Broodwar->getFrameCount();
  if (techUnit)
    t=nextFreeTime(techUnit);
  pair<int, Resources> ret;
  ret.first=t;
  ret.second.minerals=techType.mineralPrice();
  ret.second.gas=techType.gasPrice();
  reserveResources(ret);
  return ret;
}
//reserves resources for this upgrade type
pair<int, BuildOrderManager::Resources> BuildOrderManager::reserveResources(MetaUnit* techUnit, UpgradeType upgradeType)
{
  int t=Broodwar->getFrameCount();
  if (techUnit)
    t=nextFreeTime(techUnit);
  pair<int, Resources> ret;
  ret.first=t;
  ret.second.minerals=upgradeType.mineralPrice()+upgradeType.mineralPriceFactor()*this->upgradeManager->getPlannedLevel(upgradeType);
  ret.second.gas=upgradeType.gasPrice()+upgradeType.gasPriceFactor()*this->upgradeManager->getPlannedLevel(upgradeType);
  reserveResources(ret);
  return ret;
}
void BuildOrderManager::reserveResources(pair<int, BuildOrderManager::Resources> res)
{

  this->reservedResources[res.first].minerals+=res.second.minerals;
  this->reservedResources[res.first].gas+=res.second.gas;
}
//unreserves the given resources
void BuildOrderManager::unreserveResources(pair<int, BuildOrderManager::Resources> res)
{
  this->reservedResources[res.first].minerals-=res.second.minerals;
  this->reservedResources[res.first].gas-=res.second.gas;
  if (this->reservedResources[res.first].minerals==0 && this->reservedResources[res.first].gas == 0)
    this->reservedResources.erase(res.first);
}
void BuildOrderManager::enableDependencyResolver()
{
  this->dependencyResolver=true;
}
void BuildOrderManager::setDebugMode(bool debugMode)
{
  this->debugMode=debugMode;
  this->buildManager->setDebugMode(debugMode);
}


void BuildOrderManager::removeCompletedItems(PriorityLevel* p)
{
  list<TechItem>::iterator i2;
  for(list<TechItem>::iterator i=p->techs.begin();i!=p->techs.end();i=i2)
  {
    i2=i;
    i2++;
    if (i->techType!=TechTypes::None)
    {
      if (this->techManager->planned(i->techType))
      {
        p->techs.erase(i);
      }
    }
    else if (i->upgradeType!=UpgradeTypes::None)
    {
      if (this->upgradeManager->getPlannedLevel(i->upgradeType)>=i->level)
      {
        p->techs.erase(i);
      }
    }
  }
  map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem > >::iterator i3;
  for(map<BWAPI::UnitType, map<BWAPI::UnitType, UnitItem > >::iterator i=p->units.begin();i!=p->units.end();i=i3)
  {
    i3=i;
    i3++;
    map<BWAPI::UnitType, UnitItem >::iterator j2;
    for(map<BWAPI::UnitType, UnitItem >::iterator j=i->second.begin();j!=i->second.end();j=j2)
    {
      j2=j;
      j2++;
      if (j->second.getRemainingCount()==0)
        i->second.erase(j);
    }
    if (i->second.empty())
      p->units.erase(i);
  }
}

void BuildOrderManager::debug(const char* text, ...)
{
  const int BUFFER_SIZE = 1024;
  char buffer[BUFFER_SIZE];

  va_list ap;
  va_start(ap, text);
  vsnprintf_s(buffer, BUFFER_SIZE, BUFFER_SIZE, text, ap);
  va_end(ap);

  if (this->debugMode)
  {
    Broodwar->drawTextScreen(500,y,"%s",buffer);
    y+=15;
  }
}

void BuildOrderManager::addReactor(BWAPI::UnitType enemyUnit, int threshold, BWAPI::UnitType playerUnit, float ratio)
{
  // check for invalid params
  if (enemyUnit == BWAPI::UnitTypes::None || playerUnit == BWAPI::UnitTypes::None || threshold < 0 || ratio < 0.0f)
    return;

  UnitReactor temp;
  temp.threshold = threshold;
  temp.playerUnit = playerUnit;
  temp.ratio = ratio;

  // Initialize for unit type if it has not been yet
  if (this->reactors.find(enemyUnit) == this->reactors.end()){
    this->reactors[enemyUnit] = std::list<UnitReactor>();
  }

  this->reactors[enemyUnit].push_back(temp);
}

void BuildOrderManager::addUpgrade(BWAPI::UpgradeType upgrade, int threshold, BWAPI::UnitType unit, int priority, int level, BWAPI::Player* player)
{
  if (upgrade == BWAPI::UpgradeTypes::None || unit == BWAPI::UnitTypes::None)
    return;

  std::pair<BWAPI::UnitType, BWAPI::Player*> key = std::make_pair(unit, player);
  AutoUpgrade temp;
  temp.upgrade = upgrade;
  temp.threshold = threshold;
  temp.level = level;
  temp.priority = priority;

  autoUpgrades[key].push_back(temp);
}

void BuildOrderManager::addEnemyUpgrade(BWAPI::UpgradeType upgrade, int threshold, BWAPI::UnitType unit, int priority, int level)
{
  addUpgrade(upgrade, unit, threshold, level, priority, BWAPI::Broodwar->enemy());
}

bool BuildOrderManager::getMineralLimited()
{
  return this->isMineralLimited;
}

bool BuildOrderManager::getGasLimited()
{
  return this->isGasLimited;
}

int BuildOrderManager::getVisualizedMinerals()
{
  return BWAPI::Broodwar->self()->cumulativeMinerals() - this->usedMinerals;
}

int BuildOrderManager::getVisualizedGas()
{
  return BWAPI::Broodwar->self()->cumulativeGas() - this->usedGas;
}
